from abc import ABC, abstractmethod
import pandas as pd
import logging
from soccer_data_loader import Soccer1X2DataLoader, generate_test_data

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

class Soccer1X2Strategy(ABC):
    """胜平负策略抽象基类"""
    @abstractmethod
    def generate_signal(self, match_data):
        """
        生成胜平负投注信号
        :param match_data: 单场比赛数据（含赛前赔率、球队信息等）
        :return: dict，格式如{"selection": "1"/"X"/"2", "stake": 100.0}，None表示不投注
        """
        pass

class SpecificOddsPatternStrategy(Soccer1X2Strategy):
    """
    基于特定赔率规律的投注策略：
    1. 当 胜利赔率 > 平局赔率 > 失败赔率 时，押注最小赔率（失败）
    2. 当 胜利赔率 < 平局赔率 < 失败赔率 时，押注最小赔率（胜利）
    3. 不符合上述两种规律时，不投注
    每次固定投注100元
    """
    def generate_signal(self, match_data):
        # 提取三项赔率
        home_odds = match_data['home_odds']      # 胜利赔率（主胜）
        draw_odds = match_data['draw_odds']      # 平局赔率
        away_odds = match_data['away_odds']      # 失败赔率（客胜）
        
        # 情况1：胜利赔率 > 平局赔率 > 失败赔率
        if home_odds > draw_odds and draw_odds > away_odds:
            selection = "2"  # 失败（客胜）是最小赔率
            reason = f"赔率规律：胜利({home_odds:.2f})>平局({draw_odds:.2f})>失败({away_odds:.2f})，选择最小赔率"
        
        # 情况2：胜利赔率 < 平局赔率 < 失败赔率
        elif home_odds < draw_odds and draw_odds < away_odds:
            selection = "1"  # 胜利（主胜）是最小赔率
            reason = f"赔率规律：胜利({home_odds:.2f})<平局({draw_odds:.2f})<失败({away_odds:.2f})，选择最小赔率"
        
        # 不符合上述两种规律，不投注
        else:
            return None
        
        # 返回投注信号，固定投注100元
        return {
            "selection": selection,
            "stake": 100.0,
            "reason": reason
        }

class Soccer1X2BacktestEngine:
    """胜平负玩法回测引擎"""
    def __init__(self, strategy: Soccer1X2Strategy, initial_capital=10000.0):
        self.strategy = strategy
        self.initial_capital = initial_capital
        self.current_capital = initial_capital
        self.trade_history = []  # 记录所有交易
        self.bet_count = 0       # 总投注次数
        self.win_count = 0       # 赢单次数
        self.skipped_count = 0   # 未投注次数
        
    def run(self, matches: pd.DataFrame):
        """运行回测"""
        if matches is None or len(matches) == 0:
            logger.error("没有有效的比赛数据用于回测")
            return
        
        logger.info(f"开始回测，初始资金: {self.initial_capital}元，共{len(matches)}场比赛")
        
        for idx, match in matches.iterrows():
            try:
                # 生成投注信号
                signal = self.strategy.generate_signal(match)
                
                if not signal:
                    self.skipped_count += 1
                    logger.debug(
                        f"第{idx+1}场比赛 {match['home_team']} vs {match['away_team']} "
                        f"不符合投注条件，不进行投注"
                    )
                    continue
                
                if "selection" not in signal or "stake" not in signal:
                    self.skipped_count += 1
                    logger.warning(f"第{idx+1}场比赛未生成有效投注信号")
                    continue
                
                # 检查资金是否充足
                if signal["stake"] > self.current_capital:
                    self.skipped_count += 1
                    logger.warning(f"第{idx+1}场比赛资金不足，无法投注")
                    continue
                
                # 记录投注信息
                self.bet_count += 1
                bet_info = {
                    "match_id": match['match_id'],
                    "timestamp": match['timestamp'],
                    "home_team": match['home_team'],
                    "away_team": match['away_team'],
                    "selection": signal["selection"],
                    "stake": signal["stake"],
                    "odds": match[f"home_odds" if signal["selection"] == "1" 
                                 else "draw_odds" if signal["selection"] == "X" 
                                 else "away_odds"],
                    "result": match['result'],
                    "capital_before": self.current_capital,
                    "reason": signal.get("reason", "")
                }
                
                # 计算盈亏
                if signal["selection"] == match['result']:
                    # 赢单
                    profit = signal["stake"] * (bet_info["odds"] - 1)
                    self.current_capital += profit
                    self.win_count += 1
                    bet_info["profit"] = profit
                    bet_info["outcome"] = "win"
                else:
                    # 输单
                    profit = -signal["stake"]
                    self.current_capital += profit
                    bet_info["profit"] = profit
                    bet_info["outcome"] = "lose"
                
                bet_info["capital_after"] = self.current_capital
                self.trade_history.append(bet_info)
                
                # 打印单场结果
                logger.info(
                    f"比赛 {match['match_id']}: {match['home_team']} vs {match['away_team']} "
                    f"| 选择: {signal['selection']} | 赔率: {bet_info['odds']:.2f} "
                    f"| 结果: {bet_info['outcome']} | 盈亏: {profit:.2f} | 资金: {self.current_capital:.2f}"
                )
                
            except Exception as e:
                logger.error(f"处理第{idx+1}场比赛时出错: {str(e)}")
                continue
        
        # 回测结束，计算统计信息
        self._calculate_stats()
    
    def _calculate_stats(self):
        """计算回测统计信息"""
        total_matches = self.bet_count + self.skipped_count
        
        logger.info("\n===== 回测结果统计 =====")
        logger.info(f"总比赛场次: {total_matches}场")
        logger.info(f"符合投注条件的场次: {self.bet_count}场")
        logger.info(f"不符合投注条件的场次: {self.skipped_count}场")
        logger.info(f"初始资金: {self.initial_capital:.2f}元")
        logger.info(f"最终资金: {self.current_capital:.2f}元")
        
        if self.bet_count == 0:
            logger.info("未进行任何投注")
            return
        
        total_profit = self.current_capital - self.initial_capital
        win_rate = (self.win_count / self.bet_count) * 100
        roi = (total_profit / self.initial_capital) * 100
        
        logger.info(f"总盈亏: {total_profit:.2f}元")
        logger.info(f"赢单次数: {self.win_count}次")
        logger.info(f"胜率: {win_rate:.2f}%")
        logger.info(f"收益率(ROI): {roi:.2f}%")

# 执行回测
def run_strategy_backtest():
    # 生成测试数据
    test_file = generate_test_data()
    
    # 加载数据
    data_loader = Soccer1X2DataLoader()
    matches = data_loader.load_csv(test_file)
    
    if matches is None:
        logger.error("无法加载测试数据，回测终止")
        return
    
    # 创建策略实例
    strategy = SpecificOddsPatternStrategy()
    
    # 创建回测引擎并运行回测
    backtest_engine = Soccer1X2BacktestEngine(strategy, initial_capital=10000.0)
    backtest_engine.run(matches)
    
    return backtest_engine

if __name__ == "__main__":
    run_strategy_backtest()
